package com.zxyankh.raft.context;

import com.zxyankh.raft.dto.ConfigDto;
import com.zxyankh.raft.dto.Entry;
import com.zxyankh.raft.dto.ServerIpPortDto;
import com.zxyankh.raft.enums.ServerStatus;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Description: server状态的相关数据
 *
 * @author ankh
 * @since 31.10.2017
 */
public class ServerContext {
    // log entries; each entry contains command for state machine, and term
    public static final List<Entry> log = new ArrayList<>();
    // lasted term server has seen(initialized to 0, increase monotonically)
    public static final AtomicInteger currentTerm = new AtomicInteger(0);
    public static ServerStatus serverStatus = ServerStatus.FOLLOWER;
    // candidateId that received vote in current term;
    public static int votedFor = -1;
    // index of highest log entry known to be committed
    public static int commitIndex;
    // index of highest log entry applied to state machine
    public static int lastApplied;
    // for each server, index of the next log entry to send to that server
    public static List<Integer> nextIndex;
    // for each server, index of highest log entry known to be replicated on server
    public static List<Integer> matchIndex;
    // current leader in this term
    public volatile static int leader;

    private static ConfigDto configDto;

    public static void init() {
        initServerConfig();
        initServerContext();
    }

    /**
     * 初始化server配置
     */
    private static void initServerConfig() {
        String configPath = System.getProperty("user.dir") + "/config.properties";
        Path configFilePath = Paths.get(configPath);
        configDto = new ConfigDto(configPath);
        try (BufferedReader bufferedReader = Files.newBufferedReader(configFilePath)) {
            Properties properties = new Properties();
            properties.load(bufferedReader);
            properties.keySet().stream().map(String::valueOf).forEach(key -> {
                if (ServerConfigPropertiesKey.SERVER_ID.equals(key)) {
                    configDto.setServerId(Integer.parseInt(properties.getProperty(key)));
                } else if (key.matches(ServerConfigPropertiesKey.REX_SERVER)) {
                    configDto.getServerIpPortMap().put(Integer.parseInt(key.substring(key.indexOf('.') + 1, key.length())), parseServerIpPort(properties.getProperty(key)));
                } else if (ServerConfigPropertiesKey.ELECTION_TIMEOUT.equals(key)) {
                    configDto.setElectionTimeout(Long.parseLong(properties.getProperty(key)));
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void initServerContext() {

    }

    public static boolean isLeader() {
        return configDto.getServerId() == leader;
    }

    public static String getLeaderHttpAddress() {
        return configDto.getServerIpPortMap().get(leader).getIp() + ":" + configDto.getServerIpPortMap().get(leader).getHttpPort();
    }

    public static ServerIpPortDto getCurrentServerIpPort() {
        return configDto.getServerIpPortMap().get(configDto.getServerId());
    }

    private static ServerIpPortDto parseServerIpPort(String str) {
        ServerIpPortDto serverIpPortDto = new ServerIpPortDto();
        serverIpPortDto.setIp(str.substring(0, str.indexOf(':')));
        serverIpPortDto.setRpcPort(Integer.parseInt(str.substring(str.indexOf(':') + 1, str.lastIndexOf(':'))));
        serverIpPortDto.setHttpPort(Integer.parseInt(str.substring(str.lastIndexOf(':') + 1)));
        return serverIpPortDto;
    }

}
